// Copyright (c) [Year] [name of copyright holder]
// [Software Name] is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2. 
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2 
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.  
// See the Mulan PSL v2 for more details.

/*!
 * \autor captainpeng
 * \date 2018-9-5
 * \update 2019-12-17
 * \version 1.0
 * \copyright
 */

#ifndef MY_BUFFER_HPP
#define MY_BUFFER_HPP

#include<cstring>
#include"mystd.hpp"
#include"function.hpp"

namespace my {
	
    template<typename ElementType>
    class bufferT{
	
    public:
        // using elementType=typename my::typeInfo<ElementType>::type;
        // using elementPtr=typename my::typeInfo<ElementType>::ptr;
        // using elementConstPtr=typename my::typeInfo<ElementType>::constPtr;
        // using elementRef=typename my::typeInfo<ElementType>::ref;
        // using elementConstRef=typename my::typeInfo<ElementType>::constRef;
	
        using elementType = info_type_t<ElementType>;
        using elementPtr = info_ptr_t<ElementType>;
        using elementRef = info_ref_t<ElementType>;
        using elementConstRef = info_constRef_t<ElementType>;
        using elementConstPtr = info_constPtr_t<ElementType>;
	
        using iteratorType = elementPtr;
        using iteratorConstType = elementConstPtr;

        using memType = my::memory;

    private:
        using allocatorType = std::allocator<elementType>;

    private:

        static void * AllocDebug(size_t size){
            std::cout<<"alloc buffer, size = "<<size<<std::endl;
            return memFunc<elementType>::Alloc(size);
        }

        static void FreeDebug(void * p){
            std::cout<<"free buffer p == "<<p<<std::endl;
            memFunc<elementType>::Free(p);
            // memMan::GetTree().print();
        }

        inline void alloc(size_t size){
#ifdef MY_ENABLE_DEBUG
            mBegin = memType::calloc<elementType>(size, AllocDebug, FreeDebug);
#else
            mBegin = memType::calloc<elementType>(size);
#endif
            mSize = size;
        }

        // 2020-4-6
        // 改动 mEnd(elementPtr) -> mSize(size_t)
        // 变成仅仅是 std::copy 的封装，所以不再使用吧
        // 2019-12-16
        // 发现当输入的 begin 和 end 都是默认值(即都为 my::nil<elementType>())的时候
        // buffer 的内存依旧不会释放掉，而且仅仅是把 end 挪一下位置而已
        // 不知道是否需要修改，还是以后使用 buffer 的时候，注意一下即可
        // static void clone(elementPtr begin, const bufferT & buff){            
        //     std::copy(buff.begin(), buff.end(), begin);
        // }

    public:
        // 通用的 index 函数，可任意设置范围，但要求 begin 和 end 指针是 buf 中的一部分
        static inline elementRef index(long pos, elementPtr begin, long size){
            if(size <= 0) return *nil<elementType>();

            if(pos < 0){
                if((-(long)size) > pos)
                    pos = 0;
                else
                    pos = size + pos;
            }else if(pos > size){
                pos = size - 1;
            }

            return *(begin + pos);
        }

        static int comp(const bufferT & lhs, const bufferT & rhs){
            iteratorConstType it1 = lhs.begin();
            iteratorConstType end1 = lhs.end();
            iteratorConstType it2 = rhs.begin();
            iteratorConstType end2 = rhs.end();
            int result = 0;

            for(;it1 != end1 && it2 != end2; ++it1, ++it2){
                result = func<elementType>::comp((*it1), (*it2));
                if(result != 0)
                    break;
            }

            if(result == 0){
                result = func<size_t>::comp(lhs.size(), rhs.size());
            }

            return result;
        }

    public:

        bufferT():
            mBegin(nil<elementType>()),
            mSize(0)
            {}

        bufferT(size_t size){
            this->alloc(size);
            mSize = size;
        }

        bufferT(const bufferT & buff){
            this->alloc(buff.mSize);
            std::copy(buff.begin(), buff.end(), mBegin);
        }
        //note: the copy constructor will create a new memory blocks.
    
        bufferT(bufferT && buff){
            mBegin = buff.mBegin;
            mSize = buff.mSize;

            buff.mBegin = nil<elementType>();
            buff.mSize = 0;
        }

        bufferT(const std::initializer_list<elementType> & list){
			this->alloc(list.size());
            std::move(list.begin(), list.end(), mBegin);
        }
    
        bufferT & operator=(const bufferT & buff){
            if(&buff == this) return *this;
            this->free();
            this->alloc(buff.mSize);
            std::copy(buff.begin(), buff.end(), mBegin);
            return *this;
        }

        bufferT & operator=(bufferT && buff){
            if(&buff == this) return *this;

            this->free();
            mBegin = buff.mBegin;
            mSize = buff.mSize;

            buff.mBegin = nil<elementType>();
            buff.mSize = 0;

            return *this;
        }

        bufferT & operator=(const std::initializer_list<elementType> & list){
            free();
            this->alloc(list.size());
            std::move(list.begin(), list.end(), mBegin);
            return *this;
        }
	
        ~bufferT(){
            this->free();
        }

        void free(){
            if(mBegin != nil<elementType>()){
                memType::free(mBegin);
                mBegin = nil<elementType>();
                mSize = 0;
            }
        }

        inline void clear(){
            free();
        }

        //size of bufferT	
        inline long size() const {
            return (long)mSize;
        }

        //equal to -size()
        inline long ssize() const {
            return -((long)mSize);
        }

        inline iteratorType begin(){
            return mBegin;
        }

        inline iteratorType end(){
            return mBegin + mSize;
        }

        inline iteratorConstType begin() const {
            return mBegin;
        }

        inline iteratorConstType end() const {
            return mBegin + mSize;
        }

		void realloc(const size_t size){
            this->free();
            this->alloc(size);
		}
		
        void resize(const size_t size){
            if(size <= mSize) return ;

            elementPtr tmp = memType::calloc<elementType>(size);
            //当数组为空的时候，不需要move 和析构操作
            if(mBegin != nil<elementType>()){
                std::move(begin(), end(), tmp);
                // theAlloc().deallocate(mBegin,size());
                memType::free(mBegin);	    
            }
            mBegin = tmp;
            mSize = size;
        }

		inline void swap(bufferT & rhs){
            std::swap(mBegin, rhs.mBegin);
            std::swap(mSize, rhs.mSize);
		}

		inline void copy(const bufferT & rhs){
            if(mSize < rhs.mSize)
                realloc(rhs.mSize);
			std::copy(rhs.begin(), rhs.end(), mBegin);
		}

        // 引用同一个内存块
        // 按理来说吧，应该也可以引用 const 的。
        // 但是不知道怎么实现，所以算了。
        inline void ref(bufferT & rhs){
            free();
            mBegin = memType::up(rhs.mBegin);
            mSize = rhs.mSize;
        }
        
        inline elementRef operator[](const long pos){
            return index(pos, mBegin, (long)mSize);
        }

        inline elementConstRef operator[](const long pos) const {
            return index(pos, mBegin, (long)mSize);
        }

        inline elementRef operator*(){
            return *mBegin;
        }

        inline elementConstRef operator*() const {
            return *mBegin;
        }

        template<typename CALL, typename ... ARGS>
        void each(const CALL & call, ARGS && ... args){
            for(auto it = begin(); it != end(); ++it)
                if(!call((*it), std::forward<ARGS>(args)...))
                    return ;
        }

    private:
        elementPtr mBegin;
        size_t mSize;
    };

    // 比较运算符重载
    template<typename ElementType>
    bool operator==(const bufferT<ElementType> & lhs, const bufferT<ElementType> & rhs){
        return (lhs.size() == rhs.size()) && bufferT<ElementType>::comp(lhs, rhs) == 0;
    }

    template<typename ElementType>
    bool operator!=(const bufferT<ElementType> & lhs, const bufferT<ElementType> & rhs){
        return !(lhs == rhs);
    }

    template<typename ElementType>
    bool operator>(const bufferT<ElementType> & lhs, const bufferT<ElementType> & rhs){
        return bufferT<ElementType>::comp(lhs, rhs) > 0;
    }

    template<typename ElementType>
    bool operator>=(const bufferT<ElementType> & lhs, const bufferT<ElementType> & rhs){
        return bufferT<ElementType>::comp(lhs, rhs) >= 0;
    }

    template<typename ElementType>
    bool operator<(const bufferT<ElementType> & lhs, const bufferT<ElementType> & rhs){
        return bufferT<ElementType>::comp(lhs, rhs) < 0;
    }

    template<typename ElementType>
    bool operator<=(const bufferT<ElementType> & lhs, const bufferT<ElementType> & rhs){
        return bufferT<ElementType>::comp(lhs, rhs) <= 0;
    }

    template<typename ElementType>
    std::ostream & operator<<(std::ostream & os, const bufferT<ElementType> & buffer){
        os<<"{ ";
        for(size_t i = 0;i < buffer.size()-1;++i)
            os<<buffer[i]<<", ";
				
        return os<<*(buffer.end()-1)<<" }";
    }

    // buffer 封装
    template<typename T>
    using buffer = bufferT<my::info_type_t<T>>;
 
}

#endif
